
#pragma once

#include <iostream>
#include <pthread.h>
#include <queue>
using namespace std;

template<class T>
class BlockQueue
{
    static const int defalutNum = 5;
public:
    BlockQueue(int maxcap = defalutNum)
        :_maxcap(maxcap)
    {
        pthread_mutex_init(&_mutex,nullptr);
        pthread_cond_init(&c_cond,nullptr);
        pthread_cond_init(&p_cond,nullptr);
        low_water = _maxcap / 3;
        high_water = (_maxcap * 2) / 3;
    }

    //生产
    void Push(const T& in)
    {
        pthread_mutex_lock(&_mutex);
        while(_q.size() == _maxcap)//判断也是访问临界资源，也需要加锁，循环判断防止伪唤醒
        {
            //释放锁
            pthread_cond_wait(&p_cond,&_mutex);//调用时自动释放锁
        }
        _q.push(in);

        //一旦生产了，就说明队列中至少有一个元素，所以就可以唤醒等待消费的线程
        if(_q.size() > high_water)   pthread_cond_signal(&c_cond);

        pthread_mutex_unlock(&_mutex);
    }
    //消费
    T Pop()
    {
        pthread_mutex_lock(&_mutex);
        while(_q.size() == 0)
        {
            pthread_cond_wait(&c_cond, &_mutex);
        }
        T out = _q.front();
        _q.pop();

        //当已经消费了一个元素，就说明队列中至少有一个空间可以进行生产，所以唤醒等待生产的线程
        if(_q.size() < low_water)   pthread_cond_signal(&p_cond);

        pthread_mutex_unlock(&_mutex);
        return out;
    }
    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&c_cond);
        pthread_cond_destroy(&p_cond);
    }
private:
    queue<T> _q;//共享资源,由于STL容器并没有加锁，所以这里q作为共享资源需要加上锁。
    //整个队列为共享资源，被视为一个整体进行加锁访问，但可以被【切分】为多个区域，只要其余线程访问的是不同的区域，那么就能实现并发访问
    //但如果切为3个区域，并存在4个线程要访问呢？
    //就需要信号量来保证线程间的同步、互斥【进程：System V信号量；线程：POSIX信号量】
    

    int _maxcap;//阻塞队列中的元素最大个数，极值
    // int _mincap;//
    pthread_mutex_t _mutex;
    pthread_cond_t c_cond;//条件变量实现多线程间同步
    pthread_cond_t p_cond;//条件变量实现多线程间同步

    int low_water;
    int high_water;
};